﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Net.Sockets;
using SilverlightClientLibrary.Clients;
using SilverlightClientLibrary.Messages;
using System.Windows.Threading;

namespace SilverlightTcpClient
{
    public partial class MainPage : UserControl
    {
        private Player _currentPlayer;
        private List<Player> _otherPlayers;
        private TcpCommunicator _comm;

        private static Random s_random = new Random();

        public MainPage()
        {
            InitializeComponent();
            enterNamePage1.NameChanged += new Action<string>(enterNamePage1_NameChanged);
            _otherPlayers = new List<Player>();

            enterNamePage1.Attach(this);
            silverlightMessageWindow.MessageEntered += new Action<string>(silverlightMessageWindow_MessageEntered);

            _comm = new TcpCommunicator(App.Current);
            _comm.ConnectedEvent += this.Connected;
            _comm.DroppedEvent += this.Dropped;
            _comm.ClientAdded += this.PlayerAdded;
            _comm.ClientRemoved += this.PlayerRemoved;
            _comm.NewNameReceived += this.ReceivedNameChangeMessage;
            _comm.PointDataReceived += this.ReceivedPointMessage;
            _comm.ChatMessageReceived += this.ChatMessageReceived;

            CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
            this.MouseMove += new MouseEventHandler(MainPage_MouseMove);
        }

        private void ChatMessageReceived(ChatMessage cm)
        {
            silverlightMessageWindow.AddMessage(cm.ChatLine);
        }

        void silverlightMessageWindow_MessageEntered(string message)
        {
            _comm.EnqueueMessage(new ChatMessage(_currentPlayer.Name + ": " + message));
        }

        void enterNamePage1_NameChanged(string s)
        {
            Dispatcher.BeginInvoke(new Action<string>(CreatePlayer), new object[] { s });
            if(!_comm.IsConnected) _comm.Connect();
            silverlightMessageWindow.Attach(this);
        }

        private void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            foreach (Player p in _otherPlayers)
            {
                p.UpdateForeign();
            }

            if (_currentPlayer != null)
            {
                if (_comm.LittleEnqueuedMessages < 5)
                {
                    _comm.EnqueueMessage(new PointMessage(_currentPlayer.Location));
                }
            }
        }

        private void MainPage_MouseMove(object sender, MouseEventArgs e)
        {
            if (_currentPlayer != null)
            {
                if (enterNamePage1.Visibility == Visibility.Collapsed)
                    _currentPlayer.UpdateLocal(e.GetPosition(this));
                else
                    _currentPlayer.UpdateLocal(new Point());
            }
        }

        private void Connected()
        {
            if(_currentPlayer != null) silverlightMessageWindow.AddMessage("Connected as " + _currentPlayer.Name + ".");
        }

        private void Dropped()
        {
            silverlightMessageWindow.AddMessage("You have been dropped!");
            for(int i = 0; i < _otherPlayers.Count; i++)
            {
                Player p = _otherPlayers[i];
                Dispatcher.BeginInvoke(new Action<UIElement>(RemoveFromGui), new object[] { p.DrawRect });
                Dispatcher.BeginInvoke(new Action<UIElement>(RemoveFromGui), new object[] { p.TextBlock });
                _otherPlayers.Remove(p);
                i--;
            }
        }

        private void CreatePlayer(string name)
        {
            _currentPlayer = new Player(0, this.LayoutRoot, Colors.Red, name);
            _comm.EnqueueMessage(new NameChangeMessage(name));
        }

        private void ReceivedPointMessage(PointMessage pm)
        {
            Player p = GetPlayer(pm.IdOfSender);
            if (p != null) p.MoveAsync(pm.Point);
        }

        private void ReceivedNameChangeMessage(NameChangeMessage ncm)
        {
            Player p = GetPlayer(ncm.IdOfSender);
            while (p == null) { p = GetPlayer(ncm.IdOfSender); }
            p.ChangeNameAsync(ncm.NewName);
            silverlightMessageWindow.AddMessage(ncm.NewName + " has joined.");
        }

        private Player GetPlayer(byte id)
        {
            for (int i = 0; i < _otherPlayers.Count; i++)
            {
                Player p = _otherPlayers[i];
                if (id == p.Id)
                {
                    return p;
                }
            }
            return null;
        }

        private void CreateOtherPlayer(byte id, string name)
        {
            lock (_otherPlayers)
            {
                if (_otherPlayers.Where(x => x.Id == id).Count() == 0)
                {
                    Player p = new Player(id, this.LayoutRoot, Colors.Blue, name);
                    _otherPlayers.Add(p);
                }
            }
        }

        private void PlayerRemoved(ClientRemovedMessage crm)
        {
            Player p = _otherPlayers.FirstOrDefault(x => x.Id == crm.ClientRemovedId);
            if (p != null)
            {
                silverlightMessageWindow.AddMessage(p.Name + " has disconnected.");
                Dispatcher.BeginInvoke(new Action<UIElement>(RemoveFromGui), new object[] { p.DrawRect });
                Dispatcher.BeginInvoke(new Action<UIElement>(RemoveFromGui), new object[] { p.TextBlock });
                _otherPlayers.Remove(p);
            }
        }

        private void RemoveFromGui(UIElement u)
        {
            this.LayoutRoot.Children.Remove(u);
        }

        private void PlayerAdded(NewClientMessage ncm)
        {
            Dispatcher.BeginInvoke(new Action<byte, string>(CreateOtherPlayer), new object[] { ncm.ClientAddedId, "Nobody" });
            if (_currentPlayer != null)
            {
                _comm.EnqueueMessage(new NameChangeMessage(_currentPlayer.Name));
            }
        }
    }
}
